package socket;

import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

/**
 * 聊天室客户端
 */
public class Client {
    /**
     * java.net.Socket 套接字  英文原意:插座
     * Socket封装了TCP协议的通讯细节，使用它就可以与远端计算机建立连接，并基于一对输入和输出流
     * 的读写完成与远端计算机交互数据的操作。
     *
     * 将Socket想象为"电话"
     * 使用电话可以给对方拨号并建立通话。
     * 而通话的过程就是通过听筒和麦克风进行的。而听筒相当于是输入流，获取对方法发送过来的消息
     * 麦克风相当于输出流，用于将消息发送给对方的
     */
    private Socket socket;

    /**
     * 构造方法，用于初始化客户端
     */
    public Client(){
        try {
            /*
                常用的Socket构造器
                Socket(String host,int port)
                该构造器实例化Socket时就是与远端计算机建立连接的过程。如果成功建立连接则实例化
                完毕并返回该Socket对象，用它就可以与连接的计算机交互了。
                如果连接失败，构造方法会抛出异常。
                其中的两个参数分别表达的是:
                host:远端计算机的地址信息，通常输入的就是IP地址。如:192.168.1.1
                port:端口号，是一个整数，范围在0-65535之间。通常选取的都是8000以后的数字。
                我们通过ip地址可以找到网络上远端计算机，再通过端口找到该计算机上要连接的服务端
                应用程序。


                localhost:常用的一个地址，表示本机。如果在一台机器上同时测试客户端与服务端时
                就可以通过这个地址连接本机的服务端了。
             */
            System.out.println("正在连接服务端");
            /*
                如果连接的过程中没有找到服务端(可能是IP地址写错或端口写错，或网络不通)时
                会抛出异常:
                java.net.ConnectException: Connection refused: connect
                                           连接        拒绝
             */
            socket = new Socket("localhost",8088);
            System.out.println("与服务端建立连接");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 客户端开始工作的方法
     */
    public void start(){
        /*
            Socket提供了一个重要方法:
            OutputStream getOutputStream()
            该方法会返回一个字节输出流，通过该字节输出流写出的字节就可以发送给连接上的远端计算机了
         */
        try {
            //启动一个线程来负责读取服务端发送过来的消息
            ServerHandler serverHandler = new ServerHandler();
            Thread t = new Thread(serverHandler);
            t.start();

            //字节输出流，作用:将写出的字节发送给远端计算机(发给服务器了)
            OutputStream out = socket.getOutputStream();
            //转换流 作用:1衔接字节与字符流   2将写出的字符转换为字节
            OutputStreamWriter osw = new OutputStreamWriter(out, StandardCharsets.UTF_8);
            //缓冲字符输出流 作用:块写文本数据加速
            BufferedWriter bw = new BufferedWriter(osw);
            //PW 作用:1按行写出字符串 2自动行刷新
            PrintWriter pw = new PrintWriter(bw,true);

            Scanner scanner = new Scanner(System.in);
            while(true) {
                String line = scanner.nextLine();
                if("exit".equals(line)){
                    break;
                }
                pw.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                /*
                    Socket的close()方法调用后会给远端计算机发送断开消息，进行挥手动作。
                 */
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    public static void main(String[] args) {
        Client client = new Client();
        client.start();
    }

    /**
     * 客户端这边使用这个线程来负责读取服务端发送过来的消息
     */
    private class ServerHandler implements Runnable{
        public void run(){
            try {
                //通过socket获取输入流，来读取服务端发送过来的消息
                InputStream in = socket.getInputStream();
                InputStreamReader isr
                        = new InputStreamReader(in, StandardCharsets.UTF_8);
                BufferedReader br = new BufferedReader(isr);

                String line;
                //循环读取服务端发送过来的每一行字符串并输出到控制台上
                while((line = br.readLine())!=null){
                    System.out.println(line);
                }

            } catch (IOException e) {

            }
        }
    }

}
